<template>
  <div class="container">
    <h1>WebSocket 客户端</h1>
    
    <div class="connection-panel">
      <input v-model="wsUrl" placeholder="WebSocket服务器地址" />
      <input v-model="authorization" placeholder="Authorization" />
      <button @click="connect" :disabled="connected">连接</button>
      <button @click="disconnect" :disabled="!connected">断开连接</button>
      <span class="status" :class="{ connected: connected }">
        {{ connected ? '已连接' : '未连接' }}
      </span>
    </div>

    <div class="messages-container">
      <h2>消息记录</h2>
      <div class="messages" ref="messagesDiv">
        <div v-for="(msg, index) in messages" :key="index" class="message">
            <div class="message-header">
              <span class="sender">{{ msg.sender }}</span>
              <span class="time">{{ msg.time }}</span>
            </div>
            <div v-if="msg.type === 'text'" class="message-content text-message">
              <div class="message-content-text">{{ msg.content }}</div>
              <div class="message-footer">
                <div class="message-timestamp">{{ formatTimestamp(msg.timestamp) }}</div>
                <div v-if="msg.sender === username" class="message-status">
                  <span v-if="msg.status === 'sending'" class="status-sending">发送中...</span>
                  <span v-else-if="msg.status === 'sent'" class="status-sent">✓</span>
                  <span v-else-if="msg.status === 'read'" class="status-read">✓✓</span>
                </div>
                <div v-else-if="msg.status === 'unread'" class="message-status">
                  <span class="status-unread">未读</span>
                </div>
              </div>
            </div>
            <div v-else-if="msg.type === 'audio'" class="message-content audio-message">
              <audio :src="msg.content" controls></audio>
              <div class="message-footer">
                <div class="message-timestamp">{{ formatTimestamp(msg.timestamp) }}</div>
                <div v-if="msg.sender === username" class="message-status">
                  <span v-if="msg.status === 'sending'" class="status-sending">发送中...</span>
                  <span v-else-if="msg.status === 'sent'" class="status-sent">✓</span>
                  <span v-else-if="msg.status === 'read'" class="status-read">✓✓</span>
                </div>
                <div v-else-if="msg.status === 'unread'" class="message-status">
                  <span class="status-unread">未读</span>
                </div>
              </div>
            </div>
            <div v-else-if="msg.type === 'image'" class="message-content image-message">
              <img :src="msg.content" alt="图片消息" loading="lazy" @click="openImagePreview(msg.content)">
              <div class="message-footer">
                <div class="message-timestamp">{{ formatTimestamp(msg.timestamp) }}</div>
                <div v-if="msg.sender === username" class="message-status">
                  <span v-if="msg.status === 'sending'" class="status-sending">发送中...</span>
                  <span v-else-if="msg.status === 'sent'" class="status-sent">✓</span>
                  <span v-else-if="msg.status === 'read'" class="status-read">✓✓</span>
                </div>
                <div v-else-if="msg.status === 'unread'" class="message-status">
                  <span class="status-unread">未读</span>
                </div>
              </div>
            </div>
          </div>
      </div>
    </div>

    <!-- 滚动到最新消息按钮 -->
    <button 
      v-if="showScrollToBottomBtn" 
      @click="scrollToLatest" 
      class="scroll-to-bottom-btn"
      title="滚动到最新消息"
    >
      ↓
    </button>
    
    <div class="input-panel">
        <textarea v-model="messageInput" placeholder="输入消息..." @keydown.enter.prevent="handleEnterKey"></textarea>
        <div class="file-upload">
          <input type="file" ref="audioFileInput" accept="audio/*" style="display: none;" @change="handleFileSelect" />
          <button @click="selectAudioFile" :disabled="!connected">选择音频文件</button>
          <span v-if="selectedFileName">{{ selectedFileName }}</span>
        </div>
        <div class="input-actions">
          <button @click="startRecording" :disabled="recording || !connected">开始录音</button>
          <button @click="stopRecording" :disabled="!recording">停止录音</button>
          <button @click="sendTextMessage" :disabled="!connected || !messageInput.trim()">
            发送文本
          </button>
          <button @click="sendAudioFile" :disabled="!connected || !selectedFile">
            发送音频文件
          </button>
        </div>
      </div>
      
    <!-- 图片预览组件 -->
    <div v-if="showPreview" class="image-preview-overlay" @click="closeImagePreview">
        <div class="image-preview-container" @click.stop @wheel="handleWheelZoom">
          <button class="close-button" @click="closeImagePreview">&times;</button>
          <div class="image-preview-wrapper" @mousedown="startDrag">
            <img 
              :src="previewImage" 
              class="preview-image" 
              alt="预览图片"
              :style="{
                transform: `translate(${previewImageX}px, ${previewImageY}px) scale(${previewImageScale})`, 
                transition: isDragging ? 'none' : 'transform 0.2s ease',
                cursor: isDragging ? 'grabbing' : 'grab'
              }"
            >
          </div>
          <div class="preview-controls">
            <span class="hint">滚轮放大缩小 | 拖动图片 | ESC关闭 | R重置</span>
          </div>
        </div>
      </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      // 根据环境自动选择WebSocket服务器地址
      // 开发环境使用localhost，生产环境使用bwaitest域名
      wsUrl: import.meta.env.DEV ? 'ws://localhost:8081/ws/recognize' : 'https://bwaitest.bawangcanyin.com/ws/recognize',
      authorization: '',
      ws: null,
      connected: false,
      messages: [],
      messageInput: '',
      recording: false,
      mediaRecorder: null,
      audioChunks: [],
      selectedFile: null,
      selectedFileName: '',
      // 心跳包相关配置
      heartbeatInterval: 30000, // 心跳间隔时间，单位毫秒（30秒）
      heartbeatTimer: null, // 心跳定时器引用
      // 图片预览相关配置
        previewImage: null, // 当前预览的图片URL
        showPreview: false, // 是否显示图片预览
        previewImageScale: 1, // 图片缩放比例
        previewImageX: 0, // 图片X轴位置
        previewImageY: 0, // 图片Y轴位置
        isDragging: false, // 是否正在拖拽
        dragStartX: 0, // 拖拽开始时鼠标X坐标
        dragStartY: 0, // 拖拽开始时鼠标Y坐标
        showScrollToBottomBtn: false, // 是否显示滚动到底部按钮
    }
  },
  methods: {
    connect() {
      try {
        // 创建WebSocket连接URL，如果有Authorization信息则作为查询参数
        let connectUrl = this.wsUrl
        if (this.authorization.trim()) {
          // 检查URL是否已有查询参数
          const separator = this.wsUrl.includes('?') ? '&' : '?'
          connectUrl = `${this.wsUrl}${separator}Authorization=${encodeURIComponent(this.authorization.trim())}`
          }
        
        this.ws = new WebSocket(connectUrl)
        
        this.ws.onopen = () => {
          console.log('WebSocket连接已建立')
          this.connected = true
          
          // 显示连接成功信息，包含用户名（Authorization）
          if (this.authorization.trim()) {
            this.addMessage('系统', `连接成功 (用户名: ${this.authorization.trim()})`, 'text')
          } else {
            this.addMessage('系统', '连接成功', 'text')
          }
          
          // 启动心跳定时器
          this.startHeartbeat()
        }
        
        this.ws.onmessage = (event) => {
          try {
            const response = JSON.parse(event.data)
            console.log('接收到服务器消息:', response)
            // 增强消息处理逻辑，支持更多响应格式
            let messageContent = '未知消息'
            let messageType = 'text'
            
            // 处理心跳响应（pong）
            if (response.type === 'pong') {
              console.log('收到服务器心跳响应')
              return // 心跳响应不显示在消息列表中
            }
            
            // 优先判断是否为图片类型消息
            if (response.data && response.data.type === 'image') {
              // 图片类型消息
              messageType = 'image'
              // 从data.url获取图片URL
              if (response.data.url) {
                // 清理URL中的额外引号和空格
                messageContent = response.data.url.replace(/^[`'"\s]+|[`'"\s]+$/g, '')
              }
            } else {
              // 文本消息，直接从message字段获取
              if (response.message) {
                messageContent = response.message
              } else if (response.prompt) {
                messageContent = response.prompt
              } else if (response.data && response.data.message) {
                messageContent = response.data.message
              } else if (typeof response === 'string') {
                messageContent = response
              } else {
                // 兜底方案：将整个response转为字符串
                messageContent = JSON.stringify(response)
              }
              // 确保文本内容转换为字符串
              messageContent = String(messageContent || '未知消息')
            }
            
            this.addMessage('服务器', messageContent, messageType)
          } catch (e) {
            console.error('解析服务器消息失败:', e)
            // 处理非JSON格式数据或解析错误
            const content = String(event.data || '接收到未知格式消息')
            this.addMessage('服务器', content, 'text')
          }
        }
        
        this.ws.onclose = () => {
          console.log('WebSocket连接已关闭')
          this.connected = false
          // 清除心跳定时器
          this.clearHeartbeat()
          this.addMessage('系统', '连接已断开', 'text')
        }
        
        this.ws.onerror = (error) => {
          console.error('WebSocket错误:', error)
          this.addMessage('系统', '连接出错', 'text')
        }
      } catch (error) {
        console.error('创建WebSocket连接失败:', error)
        this.addMessage('系统', '创建连接失败', 'text')
      }
    },
    
    disconnect() {
      if (this.ws) {
        // 清除心跳定时器
        this.clearHeartbeat()
        this.ws.close()
        this.ws = null
      }
    },
    
    // 发送心跳包
    sendHeartbeat() {
      if (this.connected && this.ws && this.ws.readyState === WebSocket.OPEN) {
        try {
          // 发送心跳消息，type为ping
          const heartbeatMsg = JSON.stringify({ type: 'ping' })
          this.ws.send(heartbeatMsg)
          console.log('发送心跳包:', heartbeatMsg)
        } catch (error) {
          console.error('发送心跳包失败:', error)
        }
      }
    },
    
    // 启动心跳定时器
    startHeartbeat() {
      // 先清除可能存在的定时器
      this.clearHeartbeat()
      
      // 设置新的定时器
      this.heartbeatTimer = setInterval(() => {
        this.sendHeartbeat()
      }, this.heartbeatInterval)
      
      console.log('心跳定时器已启动，间隔', this.heartbeatInterval, 'ms')
    },
    
    // 清除心跳定时器
    clearHeartbeat() {
      if (this.heartbeatTimer) {
        clearInterval(this.heartbeatTimer)
        this.heartbeatTimer = null
        console.log('心跳定时器已清除')
      }
    },
    
    sendTextMessage() {
      if (!this.connected || !this.messageInput.trim()) return
      
      const message = this.messageInput.trim()
      const data = JSON.stringify({
        type: 'text',
        content: message
      })
      
      this.ws.send(data)
      this.addMessage('我', message, 'text')
      this.messageInput = ''
    },
    
    // 处理回车键发送消息
    handleEnterKey(event) {
      // 如果同时按下Shift键，则插入换行（不发送消息）
      if (event.shiftKey) {
        return
      }
      // 否则发送消息
      this.sendTextMessage()
    },
    
    async startRecording() {
      try {
        // 检查浏览器支持
        if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
          this.addMessage('系统', '浏览器不支持麦克风访问', 'text')
          return
        }
        
        // 请求麦克风权限
        this.addMessage('系统', '正在请求麦克风权限...', 'text')
        const stream = await navigator.mediaDevices.getUserMedia({ audio: { echoCancellation: true, noiseSuppression: true } })
        
        this.mediaRecorder = new MediaRecorder(stream)
        this.audioChunks = []
        
        this.mediaRecorder.addEventListener('dataavailable', (event) => {
          if (event.data.size > 0) {
            this.audioChunks.push(event.data)
          }
        })
        
        this.mediaRecorder.addEventListener('stop', () => {
          if (this.audioChunks.length > 0) {
            this.sendAudio()
          } else {
            this.addMessage('系统', '未录制到音频数据', 'text')
          }
          // 停止所有音轨
          stream.getTracks().forEach(track => track.stop())
        })
        
        this.mediaRecorder.addEventListener('error', (error) => {
          console.error('MediaRecorder错误:', error)
          this.addMessage('系统', `录音设备错误: ${error.message}`, 'text')
          this.recording = false
          stream.getTracks().forEach(track => track.stop())
        })
        
        this.mediaRecorder.start()
        this.recording = true
        this.addMessage('系统', '开始录音，请说话...', 'text')
      } catch (error) {
        console.error('录音失败:', error)
        
        // 根据错误类型显示不同的提示
        if (error.name === 'NotAllowedError' || error.name === 'PermissionDeniedError') {
          this.addMessage('系统', '用户拒绝了麦克风权限，请在浏览器设置中允许访问麦克风', 'text')
        } else if (error.name === 'NotFoundError' || error.name === 'DevicesNotFoundError') {
          this.addMessage('系统', '未找到麦克风设备，请检查设备连接', 'text')
        } else if (error.name === 'NotReadableError' || error.name === 'TrackStartError') {
          this.addMessage('系统', '麦克风被其他应用占用，请关闭其他使用麦克风的应用', 'text')
        } else {
          this.addMessage('系统', `无法访问麦克风: ${error.message}`, 'text')
        }
        this.recording = false
      }
    },
    
    stopRecording() {
      if (this.mediaRecorder && this.recording) {
        this.mediaRecorder.stop()
        this.recording = false
        this.addMessage('系统', '录音结束', 'text')
      }
    },
    
    sendAudio() {
      if (!this.connected || this.audioChunks.length === 0) return
      
      const audioBlob = new Blob(this.audioChunks, { type: 'audio/webm' })
      
      // 创建音频URL用于播放
      const audioUrl = URL.createObjectURL(audioBlob)
      this.addMessage('我', audioUrl, 'audio')
      
      // 直接发送文件流
      this.sendAudioStream(audioBlob, '录音')
    },
    
    selectAudioFile() {
      this.$refs.audioFileInput.click()
    },
    
    handleFileSelect(event) {
      const file = event.target.files[0]
      if (file) {
        this.selectedFile = file
        this.selectedFileName = file.name
      }
    },
    
    sendAudioFile() {
      if (!this.connected || !this.selectedFile) return
      
      this.addMessage('我', `正在发送文件: ${this.selectedFileName}`, 'text')
      
      // 发送文件流
      this.sendAudioStream(this.selectedFile, this.selectedFileName)
      
      // 清空选择
      this.selectedFile = null
      this.selectedFileName = ''
      this.$refs.audioFileInput.value = ''
    },
    
    sendAudioStream(blob, fileName) {
      // 创建文件流发送到WebSocket
      const reader = new FileReader()
      reader.onload = () => {
        const buffer = reader.result
        try {
          this.ws.send(buffer)
          this.addMessage('系统', `音频流已发送: ${fileName}`, 'text')
        } catch (error) {
          console.error('发送音频流失败:', error)
          this.addMessage('系统', `发送失败: ${error.message}`, 'text')
        }
      }
      reader.onerror = (error) => {
        console.error('读取音频文件失败:', error)
        this.addMessage('系统', `读取文件失败: ${error.message}`, 'text')
      }
      reader.readAsArrayBuffer(blob)
    },
    
    addMessage(sender, content, type) {
        const now = new Date()
        const time = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}:${now.getSeconds().toString().padStart(2, '0')}`
        
        // 初始化消息状态：自己发送的消息为'发送中'，接收的消息为'未读'
        const status = sender === this.username ? 'sending' : 'unread';
        
        const newMessage = {
          id: Date.now() + Math.random().toString(36).substr(2, 9), // 生成唯一ID
          sender,
          content,
          type,
          time,
          timestamp: now.toISOString(), // 添加ISO格式的时间戳
          status: status
        }
        
        this.messages.push(newMessage)
        
        // 如果是自己发送的消息，模拟发送成功
        if (sender === this.username) {
          setTimeout(() => {
            const messageIndex = this.messages.findIndex(msg => msg.id === newMessage.id);
            if (messageIndex !== -1) {
              this.messages[messageIndex].status = 'sent';
              // 可以在这里添加WebSocket确认逻辑
            }
          }, 500);
        }
        
        // 滚动到底部
        this.$nextTick(() => {
          const messagesDiv = this.$refs.messagesDiv;
          if (messagesDiv) {
            messagesDiv.scrollTop = messagesDiv.scrollHeight;
          }
        })
      },
      
      // 格式化时间显示
      formatTimestamp(timestamp) {
        if (!timestamp) return '';
        
        const date = new Date(timestamp);
        const now = new Date();
        const isToday = date.toDateString() === now.toDateString();
        
        if (isToday) {
          // 今天的消息只显示时间
          return date.toLocaleTimeString('zh-CN', { 
            hour: '2-digit', 
            minute: '2-digit' 
          });
        } else {
          // 非今天的消息显示日期和时间
          return date.toLocaleString('zh-CN', {
            month: '2-digit',
            day: '2-digit',
            hour: '2-digit',
            minute: '2-digit'
          });
        }
      },
    
    // 滚动到消息列表底部
    scrollToBottom() {
      const messagesDiv = this.$refs.messagesDiv
      if (messagesDiv) {
        messagesDiv.scrollTop = messagesDiv.scrollHeight
      }
    },
      // 打开图片预览
    openImagePreview(imageUrl) {
      this.previewImage = imageUrl;
      this.showPreview = true;
      // 禁止背景滚动
      document.body.style.overflow = 'hidden';
    },
    
    // 关闭图片预览
    closeImagePreview() {
        this.showPreview = false;
        this.previewImage = null;
        this.previewImageScale = 1; // 重置缩放比例
        this.previewImageX = 0; // 重置X轴位置
        this.previewImageY = 0; // 重置Y轴位置
        this.isDragging = false; // 重置拖拽状态
        // 恢复背景滚动
        document.body.style.overflow = 'auto';
      },
    
    // 处理滚轮放大缩小事件
     handleWheelZoom(event) {
       event.preventDefault(); // 阻止默认滚动行为
       
       // 计算缩放比例，每次滚动调整5%
       const delta = event.deltaY > 0 ? -0.05 : 0.05;
       let newScale = this.previewImageScale + delta;
       
       // 限制缩放范围在0.5到3倍之间
       newScale = Math.max(0.5, Math.min(3, newScale));
       
       this.previewImageScale = newScale;
     },
     
     // 处理键盘事件
      handleKeyDown(event) {
        // 当图片预览显示且按下ESC键时，关闭预览
        if (this.showPreview && event.key === 'Escape') {
          this.closeImagePreview();
        }
        // 按下R键重置图片位置和缩放
        if (this.showPreview && event.key === 'r') {
          this.previewImageScale = 1;
          this.previewImageX = 0;
          this.previewImageY = 0;
        }
      },
      
      // 开始拖拽
      startDrag(event) {
        // 只有在图片上点击才触发拖拽
        if (event.target.tagName === 'IMG') {
          this.isDragging = true;
          this.dragStartX = event.clientX - this.previewImageX;
          this.dragStartY = event.clientY - this.previewImageY;
          // 添加全局鼠标移动和释放事件
          document.addEventListener('mousemove', this.onDrag);
          document.addEventListener('mouseup', this.stopDrag);
          // 防止选中文本
          event.preventDefault();
        }
      },
      
      // 拖拽中
      onDrag(event) {
        if (this.isDragging) {
          this.previewImageX = event.clientX - this.dragStartX;
          this.previewImageY = event.clientY - this.dragStartY;
        }
      },
      
      // 停止拖拽
      stopDrag() {
        this.isDragging = false;
        // 移除全局事件监听
        document.removeEventListener('mousemove', this.onDrag);
        document.removeEventListener('mouseup', this.stopDrag);
      },
      
      // 处理消息区域滚动事件
      handleScroll() {
        const messagesDiv = this.$refs.messagesDiv;
        if (messagesDiv) {
          // 当用户向上滚动一定距离时显示回到底部按钮
          const isAtBottom = messagesDiv.scrollHeight - messagesDiv.scrollTop - messagesDiv.clientHeight < 100;
          this.showScrollToBottomBtn = !isAtBottom;
          
          // 标记可见的消息为已读
          this.markVisibleMessagesAsRead();
        }
      },
      
      // 标记可见的消息为已读
      markVisibleMessagesAsRead() {
        const messagesDiv = this.$refs.messagesDiv;
        if (!messagesDiv) return;
        
        const messageElements = messagesDiv.querySelectorAll('.message');
        
        messageElements.forEach((element, index) => {
          const rect = element.getBoundingClientRect();
          const isVisible = rect.top >= 0 && rect.bottom <= window.innerHeight;
          
          if (isVisible && this.messages[index] && this.messages[index].status === 'unread') {
            this.messages[index].status = 'read';
          }
        });
      },
      
      // 滚动到最新消息
      scrollToLatest() {
        const messagesDiv = this.$refs.messagesDiv;
        if (messagesDiv) {
          messagesDiv.scrollTop = messagesDiv.scrollHeight;
          this.showScrollToBottomBtn = false;
        }
      },
  },
  
  mounted() {
      // 组件挂载时的初始化逻辑
      this.connect();
      // 添加键盘事件监听
      document.addEventListener('keydown', this.handleKeyDown);
      // 监听滚动事件，用于显示/隐藏回到底部按钮
      this.$nextTick(() => {
        const messagesDiv = this.$refs.messagesDiv;
        if (messagesDiv) {
          messagesDiv.addEventListener('scroll', this.handleScroll);
        }
      });
    },
    
    beforeUnmount() {
      // 确保清除心跳定时器并断开连接
      this.clearHeartbeat()
      this.disconnect()
      // 组件卸载时移除事件监听，避免内存泄漏
      document.removeEventListener('keydown', this.handleKeyDown);
      const messagesDiv = this.$refs.messagesDiv;
      if (messagesDiv) {
        messagesDiv.removeEventListener('scroll', this.handleScroll);
      }
    }
}
</script>

<style scoped>
.container {
  max-width: 800px;
  margin: 0 auto;
  padding: 20px;
  font-family: Arial, sans-serif;
}

h1 {
  color: #333;
  text-align: center;
}

.connection-panel {
  display: flex;
  gap: 10px;
  margin-bottom: 20px;
  align-items: center;
}

.connection-panel input {
  flex: 1;
  padding: 8px;
  border: 1px solid #ddd;
  border-radius: 4px;
  min-width: 150px;
}

.connection-panel button {
  padding: 8px 16px;
  background-color: #4CAF50;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

.connection-panel button:hover:not(:disabled) {
  background-color: #45a049;
}

.connection-panel button:disabled {
  background-color: #cccccc;
  cursor: not-allowed;
}

.status {
  margin-left: auto;
  padding: 4px 8px;
  border-radius: 4px;
  font-size: 14px;
}

.status.connected {
  background-color: #4CAF50;
  color: white;
}

.messages-container {
  border: 1px solid #ddd;
  border-radius: 4px;
  height: 400px;
  overflow: hidden;
  margin-bottom: 20px;
}

.messages-container h2 {
  margin: 0;
  padding: 10px;
  background-color: #f5f5f5;
  border-bottom: 1px solid #ddd;
  font-size: 18px;
}

.messages {
  height: calc(100% - 50px);
  overflow-y: auto;
  padding: 10px;
}

.message {
      margin-bottom: 15px;
      padding: 10px;
      background-color: #f9f9f9;
      border-radius: 4px;
      position: relative;
    }
    
    .message-footer {
       display: flex;
       justify-content: space-between;
       align-items: center;
       margin-top: 4px;
     }
     
     .message-timestamp {
       font-size: 11px;
       color: #999;
       line-height: 1.2;
     }
     
     .message-status {
       font-size: 11px;
       margin-left: 8px;
     }
     
     .status-sending {
       color: #999;
     }
     
     .status-sent {
       color: #666;
     }
     
     .status-read {
       color: #1890ff;
       font-weight: bold;
     }
     
     .status-unread {
       color: #1890ff;
       background-color: rgba(24, 144, 255, 0.1);
       padding: 2px 6px;
       border-radius: 10px;
       font-size: 10px;
     }
    
    .text-message {
      display: flex;
      flex-direction: column;
    }
    
    .message-content-text {
      word-break: break-word;
    }
    
    .image-message {
      display: flex;
      flex-direction: column;
      align-items: flex-start;
    }
    
    .image-message .message-timestamp {
      margin-top: 4px;
      align-self: flex-end;
    }
    
    .audio-message {
      display: flex;
      flex-direction: column;
    }

.message-header {
  display: flex;
  justify-content: space-between;
  margin-bottom: 5px;
  font-size: 12px;
}

.sender {
  font-weight: bold;
  color: #4CAF50;
}

.time {
  color: #999;
}

.message-content {
  word-break: break-word;
}

.audio-message audio {
  width: 100%;
  max-width: 300px;
}

.image-message img {
  max-width: 100%;
  max-height: 300px;
  border-radius: 4px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  cursor: pointer;
  transition: transform 0.2s ease;
}

.image-message img:hover {
  transform: scale(1.02);
}

/* 图片预览样式 */
.image-preview-overlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.9);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 1000;
  cursor: pointer;
}

.image-preview-container {
      position: relative;
      max-width: 90%;
      max-height: 90%;
      margin: auto;
      background-color: white;
      border-radius: 8px;
      overflow: hidden;
      box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
      cursor: default;
    }
    
    .image-preview-wrapper {
      position: relative;
      display: flex;
      justify-content: center;
      align-items: center;
      overflow: visible;
      min-height: 400px;
    }
    
    .preview-controls {
      position: absolute;
      bottom: 20px;
      left: 50%;
      transform: translateX(-50%);
      background-color: rgba(0, 0, 0, 0.7);
      color: white;
      padding: 8px 16px;
      border-radius: 20px;
      font-size: 12px;
    }
    
    .preview-image {
      user-select: none;
      -webkit-user-drag: none;
    }

.preview-image {
  max-width: 100%;
  max-height: 90vh;
  object-fit: contain;
  border-radius: 4px;
}

.close-button {
  position: absolute;
  top: -40px;
  right: 0;
  background-color: transparent;
  color: white;
  border: none;
  font-size: 32px;
  cursor: pointer;
  padding: 0;
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.close-button:hover {
  opacity: 0.7;
}

.input-panel {
    display: flex;
    flex-direction: column;
    gap: 10px;
  }

  .input-panel textarea {
    width: 100%;
    height: 100px;
    padding: 10px;
    border: 1px solid #ddd;
    border-radius: 4px;
    resize: none;
  }

  .file-upload {
    display: flex;
    align-items: center;
    gap: 10px;
  }

  .file-upload button {
    padding: 8px 16px;
    background-color: #2196F3;
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
  }

  .file-upload button:hover:not(:disabled) {
    background-color: #1976D2;
  }

  .file-upload button:disabled {
    background-color: #cccccc;
    cursor: not-allowed;
  }

  .file-upload span {
    font-size: 14px;
    color: #666;
    flex: 1;
  }

.input-actions {
  display: flex;
  gap: 10px;
  justify-content: flex-end;
}

.input-actions button {
  padding: 8px 16px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-size: 14px;
}

.input-actions button:nth-child(1) {
  background-color: #2196F3;
  color: white;
}

.input-actions button:nth-child(2) {
  background-color: #f44336;
  color: white;
}

.input-actions button:nth-child(3) {
    background-color: #4CAF50;
    color: white;
  }

  .input-actions button:nth-child(4) {
    background-color: #FF9800;
    color: white;
  }

.input-actions button:hover:not(:disabled) {
  opacity: 0.8;
}

.input-actions button:disabled {
  background-color: #cccccc;
  cursor: not-allowed;
}

/* 滚动到最新消息按钮样式 */
.scroll-to-bottom-btn {
  position: fixed;
  bottom: 80px;
  right: 20px;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background-color: #1890ff;
  color: white;
  border: none;
  cursor: pointer;
  font-size: 18px;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
  z-index: 100;
  opacity: 0.9;
  transition: all 0.3s ease;
}

.scroll-to-bottom-btn:hover {
  background-color: #40a9ff;
  transform: scale(1.1);
  opacity: 1;
}

.scroll-to-bottom-btn:active {
  transform: scale(0.95);
}
</style>